package sma.iss;

import java.io.IOException;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import sma.iss.util.IssException;

public class GeneticModelRun {

	/** Logger for this class. */
	protected final static Logger LOGGER = LoggerFactory.getLogger(GeneticModelRun.class);

	/** Example test cases. */
	public final static double[] ALL_BETA_EXAMPLE = new double[] { 70, -72 };

	/** All values of beta to be tested, Provisional test cases. */
	public final static double[] ALL_BETA_PROV = new double[] { -74, 72, 74, -70 };

	// private static double YAW_DEFAULT = 2.1;
	// for -74
	/** YAW angle. */
	protected static double YAW_DEFAULT = 0.0;

	// pire : -74, 74, -70, 72
	@Test
	public void runBuildDynamicModelM74() throws IOException {
		runBetaArray(new double[] { -74 });
		// String simName = runBuildDynamicModel(-74, YAW_DEFAULT);
		// scanYaw(-74);
		// String simName = runBuildDynamicModel(-74, 0.2);
		// simName = runEnhanceExisting(simName, 8);
	}

	@Test
	public void runBuildDynamicModel74() throws IOException {
		runBetaArray(new double[] { 74 });
		// scanYaw(74);
		// String simName = runBuildDynamicModel(74, 0.2);
		// simName = runEnhanceExisting(simName, 8);
	}

	@Test
	public void runBuildDynamicModelM70() throws IOException {
		runBetaArray(new double[] { -70 });
		// scanYaw(-70);
	}

	@Test
	public void runBuildDynamicModelM750() throws IOException {
		// runBetaArray(new double[] { -70 });
		// scanYaw(-70);
		String simName = runBuildDynamicModel(-75, 0.0);
		simName = runEnhanceExisting(simName, 4, 8);
	}

	@Test
	public void runBuildDynamicModel72() throws IOException {
		runBetaArray(new double[] { 72 });
		// scanYaw(72);
	}

	// Example
	@Test
	public void runBuildDynamicModel70() {
		runBetaArray(new double[] { 70 });
	}

	@Test
	public void runBuildDynamicModel71() {
		runBetaArray(new double[] { 71 });
	}

	@Test
	public void runBuildDynamicModelM71() {
		runBetaArray(new double[] { -71 });
	}

	@Test
	public void runBuildDynamicModel75D() throws IOException {
		// runBetaArray(new double[] { 75 });
		String simName = runBuildDynamicModel(75, YAW_DEFAULT);
	}

	@Test
	public void runBuildDynamicModelM72() {
		runBetaArray(new double[] { -72 });
	}

	@Test
	public void runBuildDynamicModelXX() throws IOException {
		// runBetaArray(new double[] { -74 });
		// String simName = runBuildDynamicModel(-74, YAW_DEFAULT);
		// scanYaw(-74);
		String simName = runBuildDynamicModel(-70, 0.2);
		simName = runEnhanceExisting(simName, 4, 18);
	}

	@Test
	public void runBuildDynamicModelPart1() {
		runBetaArray(new double[] { -74, 72 });
	}

	@Test
	public void runBuildDynamicModelPart2() {
		runBetaArray(new double[] { 74, -70 });
	}

	@Test
	public void runBuildDynamicModel4() {
		double[] allBetas = new double[] { 74, -74, -70, 72 };
		runBetaArray(allBetas);
	}

	private void scanYaw(double beta) throws IOException {
		for (int y = 1; y <= 7; y++) {
			LOGGER.info("yaw={}", y);
			String simName = runBuildDynamicModel(beta, y);
			simName = runEnhanceExisting(simName, 4, 18);
		}
	}

	protected void runBetaArray(double[] allBetas) {
		for (double beta : allBetas) {
			try {
				int nbinterleave = 0;
				String simName = runBuildDynamicModel(beta, YAW_DEFAULT);
				LOGGER.info("------------------>                         runEnhanceExisting");
				simName = runEnhanceExisting(simName, 4, 30);
				nbinterleave++;
				// LOGGER.info("------------------>                         nbinterleave={}", nbinterleave);
				// simName = runEnhanceInterleave(simName, 4);
				// LOGGER.info("------------------>                         runEnhanceExisting");
				// simName = runEnhanceExisting(simName, 8);
				String lastNotNull = "";
				while (simName != null) {
					nbinterleave++;
					LOGGER.info("------------------>                         nbinterleave={}", nbinterleave);
					lastNotNull = simName;
					simName = runEnhanceInterleave(simName, 2);
				}

				// last optim
				LOGGER.info("------------------>                         last runEnhanceExisting");
				simName = runEnhanceExisting(lastNotNull, 4, 30);
			} catch (Exception e) {
				LOGGER.error("Error with beta " + beta, e);
			}
		}
	}

	@Test
	public void runBuildDynamicModelSystem1() {
		double[] allBetas = new double[] { 71, 73, 75 };
		runBetaArray(allBetas);
	}

	@Test
	public void runBuildDynamicModelSystem2() {
		double[] allBetas = new double[] { -71, -73, -75 };
		runBetaArray(allBetas);
	}

	@Test
	public void runBuildDynamicModelSystem0() {
		double[] allBetas = new double[] { -71 };
		runBetaArray(allBetas);
	}

	public String runBuildDynamicModel(double beta, double yaw) throws IOException {

		long startTime = System.currentTimeMillis();
		GeneticModel dynamicModel = new GeneticModel();
		dynamicModel.nbKeepForNextTurn = 1;
		dynamicModel.nbAdvance = 1;
		dynamicModel.setBeta(beta);
		dynamicModel.setYaw(yaw);

		// first calculation, let's keep a good start
		// dynamicModel.nbGeneration = 12;
		dynamicModel.nbGeneration = 12;
		LOGGER.info("runBuild Genetic Model, nbGen={}", dynamicModel.nbGeneration);
		// because of speed of MAX_SARJ_SPEED = 0.15 you need 20 min to find the best position
		// dynamicModel.calculateListOfPosition(-37);
		// dynamicModel.calculateListOfPosition(-17);
		dynamicModel.calculateListOfPosition(-22);
		// dynamicModel.calculateListOfPosition(-17);
		// dynamicModel.calculateListOfPosition(-14);
		// dynamicModel.calculateListOfPosition(-10);
		// dynamicModel.calculateListOfPosition(-5);
		// dynamicModel.calculateListOfPosition(-3);
		// dynamicModel.calculateListOfPosition(-1);

		int nbPrepare = dynamicModel.mapListPos.size();
		dynamicModel.calculateListOfPosition(0);
		// dynamicModel.nbGeneration = 8;
		dynamicModel.nbGeneration = 30;
		// max min in step is 12 (because speed of 0.25)
		int mnPerStep = 5;

		// int nbMaxstep = 4 + 1;
		dynamicModel.iterateOnMinutes(mnPerStep);

		if (!dynamicModel.validateAllTransitions()) {
			throw new IssException("Invalid model");
		}
		LOGGER.info("All positions={}", dynamicModel.getPositions());
		dynamicModel.saveSimulation();
		dynamicModel.saveResultInCsv("Genetic multi nbGen=" + dynamicModel.nbGeneration + " mn/step=" + mnPerStep + " nbPrepare="
				+ nbPrepare);

		long timeTaken = System.currentTimeMillis() - startTime;
		LOGGER.info("end in {} ms, {} calculations (average {} ms)", timeTaken, dynamicModel.nbCaculationDone(),
				timeTaken / dynamicModel.nbCaculationDone());
		return dynamicModel.getSimulationName();
	}

	// private void iteranteOnMinutes(GeneticModel dynamicModel, int mnPerStep) {
	// Collection<Position> lastList = null;
	// for (int minute = mnPerStep; minute < 92; minute += mnPerStep) {
	// if (minute <= 92 - (mnPerStep / 2)) {
	// lastList = dynamicModel.calculateListOfPosition(minute);
	// } else {
	// LOGGER.info("Mn {} is too much close to minute 92/0", minute);
	// }
	//
	// }
	// dynamicModel.fillMapMinutes(lastList.iterator().next());
	// }

	@Test
	public void testReadAndSaveFile() throws IOException {

		String simulationName = "b_75.0_y_0.0_0211233743";
		long startTime = System.currentTimeMillis();
		GeneticModel dynamicModel = new GeneticModel();
		// dynamicModel.nbGeneration = 5;
		dynamicModel.loadSimulation(simulationName);

		LOGGER.info("All positions={}", dynamicModel.getPositions());
		// dynamicModel.saveSimulation();
		dynamicModel.saveResultInCsv("initialy load from" + simulationName);

		long timeTaken = System.currentTimeMillis() - startTime;
		LOGGER.info("end in {} ms, {} calculations (average {} ms)", timeTaken, dynamicModel.nbCaculationDone);
		// LOGGER.info("end in {} ms, {} calculations (average {} ms)", timeTaken, dynamicModel.nbCaculationDone,
		// timeTaken
		// / dynamicModel.nbCaculationDone);
	}

	@Test
	public void runEnhanceOnePosition() throws IOException {
		String simulationName = "b_-70.0_y_1.9_0205093521";
		long startTime = System.currentTimeMillis();
		GeneticModel dynamicModel = new GeneticModel();
		dynamicModel.enhanceMode = true;
		dynamicModel.nbGeneration = 7;
		dynamicModel.loadSimulation(simulationName);

		// try to optimize one new position:
		dynamicModel.enhanceOneNewPosition(80);
		if (!dynamicModel.validateAllTransitions()) {
			throw new IssException("Invalid model");
		}
		dynamicModel.interpolateAllMissing();
		dynamicModel.validateAllTransitions();

		LOGGER.info("All positions={}", dynamicModel.getPositions());
		// dynamicModel.saveSimulation();
		dynamicModel.saveResultInCsv("initialy load from" + simulationName);

		long timeTaken = System.currentTimeMillis() - startTime;
		LOGGER.info("end in {} ms, {} calculations (average {} ms)", timeTaken, dynamicModel.nbCaculationDone);
		// LOGGER.info("end in {} ms, {} calculations (average {} ms)", timeTaken, dynamicModel.nbCaculationDone,
		// timeTaken
		// / dynamicModel.nbCaculationDone);
	}


	@Test
	public void runEnhanceExisting() throws IOException {
		// String simulationName = "b_-74.0_y_0.1_0202083718";
		String simulationName = "b_74.0_y_0.0_0213183007";
		runEnhanceExisting(simulationName, 3, 30);
	}

	private String runEnhanceExisting(String simulationName, int nbNoImproveMaxPerTurn, int nbGen) throws IOException {
		long startTime = System.currentTimeMillis();
		GeneticModel dynamicModel = new GeneticModel();
		dynamicModel.enhanceMode = true;
		dynamicModel.nbKeepForNextTurn = 1;
		dynamicModel.nbAdvance = 1;
		dynamicModel.nbGeneration = nbGen;
		dynamicModel.improveMandatory = true;
		dynamicModel.nbNoImproveMaxPerTurn = nbNoImproveMaxPerTurn;
		dynamicModel.loadSimulation(simulationName);

		// try to optimize one new position:
		dynamicModel.enhanceExistingPosition();

		LOGGER.info("All positions={}", dynamicModel.getPositions());
		if (!dynamicModel.validateAllTransitions()) {
			throw new IssException("Invalid model produced");
		}
		dynamicModel.saveSimulation();
		// try to optimize one new position:
		boolean isValid = dynamicModel.validateAllTransitions();
		LOGGER.info("isValid={}", isValid);

		dynamicModel.interpolateAllMissing();
		isValid = dynamicModel.validateAllTransitions();
		LOGGER.info("interpolated isValid={}", isValid);

		dynamicModel.saveResultInCsv("initialy load from " + simulationName + ", enhanceExistingPosition");

		long timeTaken = System.currentTimeMillis() - startTime;
		// LOGGER.info("end in {} ms, {} calculations (average {} ms)", timeTaken, dynamicModel.nbCaculationDone);
		LOGGER.info("runEnhanceSimulationExisting end in {} ms, {} calculations (average {} ms)", timeTaken,
				dynamicModel.nbCaculationDone, timeTaken
 / dynamicModel.nbCaculationDone());
		return dynamicModel.getSimulationName();
	}

	@Test
	public void runEnhanceInterleave() throws IOException {
		// String simulationName = "b_-74.0_y_0.1_0202083718";
		String simulationName = "b_75.0_y_0.0_0212214404";
		runEnhanceInterleave(simulationName, 4);
	}

	private String runEnhanceInterleave(String simulationName, int nbGen) throws IOException {
		long startTime = System.currentTimeMillis();
		GeneticModel dynamicModel = new GeneticModel();
		dynamicModel.enhanceMode = true;
		dynamicModel.nbGeneration = nbGen;
		dynamicModel.nbNoImproveMaxPerTurn = 2;
		dynamicModel.loadSimulation(simulationName);

		// try to optimize one new position:
		int nbAdded = dynamicModel.enhanceInterleavePosition();
		if (nbAdded == 0) {
			LOGGER.info("nothing added");
			return null;
		}
		LOGGER.info("All positions={}", dynamicModel.getPositions());
		if (!dynamicModel.validateAllTransitions()) {
			throw new IssException("Invalid model produced");
		}

		dynamicModel.saveSimulation();
		dynamicModel.saveResultInCsv("initialy load from " + simulationName + " then Interleaved with " + nbAdded + " added");

		long timeTaken = System.currentTimeMillis() - startTime;
		// LOGGER.info("end in {} ms, {} calculations (average {} ms)", timeTaken, dynamicModel.nbCaculationDone);
		LOGGER.info("runEnhanceInterleave end in {} ms, {} calculations (average {} ms)", timeTaken, dynamicModel.nbCaculationDone,
				timeTaken / dynamicModel.nbCaculationDone());
		return dynamicModel.getSimulationName();
	}

}
